-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: enable null reference type support #2146
base: main
Are you sure you want to change the base?
Conversation
@@ -274,7 +274,7 @@ | |||
if ((version.is2_0() || version.is3_0()) && (doc.Paths == null)) | |||
{ | |||
// paths is a required field in OpenAPI 2.0 and 3.0 but optional in 3.1 | |||
RootNode.Context.Diagnostic.Errors.Add(new OpenApiError("", $"Paths is a REQUIRED field at {RootNode.Context.GetLocation()}")); | |||
RootNode?.Context.Diagnostic.Errors.Add(new OpenApiError("", $"Paths is a REQUIRED field at {RootNode.Context.GetLocation()}")); |
Check warning
Code scanning / CodeQL
Dereferenced variable may be null
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI 10 days ago
To fix the problem, we need to ensure that RootNode
is not null before accessing its Context
property. The best way to do this is to add a null check for RootNode
before the line where it is dereferenced. If RootNode
is null, we can either log an error or handle it appropriately to avoid the NullReferenceException
.
-
Copy modified lines R277-R280
@@ -276,3 +276,6 @@ | ||
// paths is a required field in OpenAPI 2.0 and 3.0 but optional in 3.1 | ||
RootNode?.Context.Diagnostic.Errors.Add(new OpenApiError("", $"Paths is a REQUIRED field at {RootNode.Context.GetLocation()}")); | ||
if (RootNode != null) | ||
{ | ||
RootNode.Context.Diagnostic.Errors.Add(new OpenApiError("", $"Paths is a REQUIRED field at {RootNode.Context.GetLocation()}")); | ||
} | ||
} |
} | ||
else if (_artifactsRegistry.TryGetValue(uri, out var artifact)) | ||
{ | ||
return (T)(object)artifact; |
Check warning
Code scanning / CodeQL
Useless upcast
if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) && | ||
(!childItem.Required?.Contains(discriminatorName) ?? false)) | ||
{ | ||
return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName); | ||
} | ||
else | ||
{ | ||
return true; | ||
} |
Check notice
Code scanning / CodeQL
Missed ternary opportunity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work here!
I've only done a partial review at this point starting with the interfaces, since changes there will require reviewing implementations again.
@@ -19,7 +19,7 @@ internal interface IOpenApiVersionService | |||
/// <param name="summary">The summary of the reference.</param> | |||
/// <param name="description">A reference description</param> | |||
/// <returns>The <see cref="OpenApiReference"/> object or null.</returns> | |||
OpenApiReference ConvertToOpenApiReference(string reference, ReferenceType? type, string summary = null, string description = null); | |||
OpenApiReference? ConvertToOpenApiReference(string reference, ReferenceType? type, string? summary = null, string? description = null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we accept a nullable reference type here? shouldn't it be required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be null in the case of an externally referenced file:
OpenAPI.NET/src/Microsoft.OpenApi/Reader/V2/OpenApiV2VersionService.cs
Lines 142 to 151 in 85c3a4e
// Either this is an external reference as an entire file | |
// or a simple string-style reference for tag and security scheme. | |
if (type == null) | |
{ | |
// "$ref": "Pet.json" | |
return new() | |
{ | |
ExternalResource = segments[0] | |
}; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand this case? "it's something from another file but we don't know what it is" ??? how is that possible since the reference itself will be in a "known section" (i.e. the reference will be where a schema is, etc...)
@@ -27,16 +27,16 @@ protected OpenApiExtensibleDictionary():this(null) { } | |||
/// <param name="dictionary">The generic dictionary.</param> | |||
/// <param name="extensions">The dictionary of <see cref="IOpenApiExtension"/>.</param> | |||
protected OpenApiExtensibleDictionary( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it be better to make the dictionary parameter non nullable and optional with a default value of []
(if that's possible) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's possible. However I didn't want to tamper with/change model data as it might have side effects during serialization
|
||
/// <summary> | ||
/// External resource in the reference. | ||
/// It maybe: | ||
/// 1. a absolute/relative file path, for example: ../commons/pet.json | ||
/// 2. a Url, for example: http://localhost/pet.json | ||
/// </summary> | ||
public string ExternalResource { get; init; } | ||
|
||
public string? ExternalResource { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving the comment here because I can't below.
I'm not sure Type should be nullable
@@ -136,7 +140,7 @@ public void SerializeAsV2(IOpenApiWriter writer) | |||
|
|||
foreach (var example in Content | |||
.Where(mediaTypePair => mediaTypePair.Value.Examples != null && mediaTypePair.Value.Examples.Any()) | |||
.SelectMany(mediaTypePair => mediaTypePair.Value.Examples)) | |||
.SelectMany(mediaTypePair => mediaTypePair.Value.Examples!)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use bang operators , they usually lead to bugs.
Add an OfType() after the select many instead, not only it'll filter the null values at runtime, it'll fix the compiler complaining about nullable values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using OfType() still doesn't work. There's still a possible null reference return at the SelectMany():
foreach (var example in Content
.Where(mediaTypePair => mediaTypePair.Value.Examples is not null && mediaTypePair.Value.Examples.Any())
.SelectMany(mediaTypePair => mediaTypePair.Value.Examples)
.OfType<KeyValuePair<string, IOpenApiExample>>())
{
writer.WritePropertyName(example.Key);
example.Value.SerializeAsV2(writer);
}
Co-authored-by: Vincent Biret <[email protected]>
|
This PR:
nullable
feature in the core and readers projectFixes #1202